#ifndef DATAOBJECT_H_
#define DATAOBJECT_H_

#include "scHeader.h"
#include <QString>
#include <boost/multi_index_container.hpp>
#include <boost/multi_index/member.hpp>
#include <boost/multi_index/mem_fun.hpp>
#include <boost/multi_index/ordered_index.hpp>
#include "ColDesc.h"
#include "DataCell.h"

namespace sybilpm
{
struct DataObject_ : public std::vector<DataCell>
{
	DataObject_();
	bool modifyData(const QXmlAttributes & attributes);
	void toXml(QXmlStreamWriter * writer) const;
	bool isDirty() const {return dirty;}
	void clearDirty() {dirty=false;}
	void setDirty() {dirty=true;}
	bool isTouched() const {return touched;}
	void clearTouched() {touched=false;}
	void setTouched() {touched=true;}
private:
	bool dirty, touched;
};

/**
 * generic data like a table row
 */
class DataObject
{
public:
	struct PkTag{};
	struct IkTag{};
	struct Fk1Tag{};
	struct Fk2Tag{};
	struct Fk3Tag{};

	DataObject(PkType primaryKey, const ColDescs & cds, const QXmlAttributes & attributes);
	virtual ~DataObject();
	PkType getPk() const       {return pk;}
	void   setPk(PkType newPk) {pk=newPk;}
	PkType getIk() const       {return ik;}
	void   setIk(PkType newIk) {ik=newIk;}
	TimeType getIks() const            {return ikSort;}
	void     setIks(TimeType newIks)   {ikSort=newIks;}
	TimeType getIks2() const           {return ikSort2;}
	void     setIks2(TimeType newIks_) {ikSort2=newIks_;}
	PkType getFkn(int n) const {return n<MAX_FK_PER_TABLE?fk[n]:0;}
	void   setFkn(int n, PkType newFk) {if(n<MAX_FK_PER_TABLE){fk[n]=newFk;}}
	PkType getFk1() const {return fk[0];}
	PkType getFk2() const {return fk[1];}
	PkType getFk3() const {return fk[2];}
	DataObject_ & getPayload() const {return const_cast<DataObject_ &>(pl);}
private:
	PkType pk, ik, fk[MAX_FK_PER_TABLE];
	TimeType ikSort, ikSort2;
	DataObject_ pl;
};

typedef boost::multi_index_container<DataObject,
	boost::multi_index::indexed_by<
		boost::multi_index::ordered_unique<boost::multi_index::tag<DataObject::PkTag>,
			BOOST_MULTI_INDEX_CONST_MEM_FUN(DataObject,PkType,getPk)>,
		boost::multi_index::ordered_non_unique<boost::multi_index::tag<DataObject::IkTag>,
			BOOST_MULTI_INDEX_CONST_MEM_FUN(DataObject,PkType,getIk)>,
		boost::multi_index::ordered_non_unique<boost::multi_index::tag<DataObject::Fk1Tag>,
			BOOST_MULTI_INDEX_CONST_MEM_FUN(DataObject,PkType,getFk1)>,
		boost::multi_index::ordered_non_unique<boost::multi_index::tag<DataObject::Fk2Tag>,
			BOOST_MULTI_INDEX_CONST_MEM_FUN(DataObject,PkType,getFk2)>,
		boost::multi_index::ordered_non_unique<boost::multi_index::tag<DataObject::Fk3Tag>,
			BOOST_MULTI_INDEX_CONST_MEM_FUN(DataObject,PkType,getFk3)> >
> DataObjects;

typedef       DataObjects::index<DataObject::PkTag >::type DOsPkIdx;
typedef const DataObjects::index<DataObject::PkTag >::type DOsCPkIdx;
typedef       DataObjects::index<DataObject::IkTag >::type DOsIkIdx;
typedef const DataObjects::index<DataObject::IkTag >::type DOsCIkIdx;
typedef       DataObjects::index<DataObject::Fk1Tag>::type DOsFk1Idx;
typedef const DataObjects::index<DataObject::Fk1Tag>::type DOsCFk1Idx;
typedef       DataObjects::index<DataObject::Fk2Tag>::type DOsFk2Idx;
typedef const DataObjects::index<DataObject::Fk2Tag>::type DOsCFk2Idx;
typedef       DataObjects::index<DataObject::Fk3Tag>::type DOsFk3Idx;
typedef const DataObjects::index<DataObject::Fk3Tag>::type DOsCFk3Idx;

struct DataObjectModifier
{
	DataObjectModifier() :
		active(false), usePk(false), useIk(false), useIks(false), useIks2(false)
	{
		for(int i(0); i<MAX_FK_PER_TABLE; ++i)
			useFk[i]=false;
	}
	bool isActive() const {return active;}
	void setPk(PkType newPk) {active=true; usePk=true; pk=newPk;}
	void setIk(PkType newIk) {active=true; useIk=true; ik=newIk;}
	void setFk(int fkIdx, PkType newFk) {if(fkIdx<MAX_FK_PER_TABLE) {active=true; useFk[fkIdx]=true; fk[fkIdx]=newFk;}}
	void setIks(TimeType newIks) {active=true; useIks=true; iks=newIks;}
	void setIks_(TimeType newIks_) {active=true; useIks2=true; iks2=newIks_;}
	std::string toString() const
	{
		std::ostringstream os;
		os<<"DataObjectModifier(";
		if(usePk) os<<"pk->"<<pk;
		if(useIk) os<<"ik->"<<ik;
		for(int i(0); i<MAX_FK_PER_TABLE; ++i)
			if(useFk[i]) os<<"fk["<<i<<"]->"<<fk[i];
		if(useIks) os<<"iks->"<<iks;
		if(useIks2) os<<"iks2->"<<iks2;
		os<<")";
		return os.str();
	}
	void operator()(DataObject & dobj)
	{
		if(!active) return;
		if(usePk) dobj.setPk(pk);
		if(useIk) dobj.setIk(ik);
		for(int i(0); i<MAX_FK_PER_TABLE; ++i)
			if(useFk[i]) dobj.setFkn(i, fk[i]);
		if(useIks) dobj.setIks(iks);
		if(useIks2) dobj.setIks2(iks2);
	}
private:
	PkType pk, ik, fk[MAX_FK_PER_TABLE];
	TimeType iks, iks2;
	bool active, usePk, useIk, useFk[MAX_FK_PER_TABLE], useIks, useIks2;
};
} // namespace sybilpm

namespace std
{
ostream& operator<< (ostream& out, const sybilpm::DataObjectModifier & dom);
}

#endif /* DATAOBJECT_H_ */
